package com.fingard.dsp.bank.directbank.abc23.util;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.math.BigInteger;
import java.security.KeyFactory;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.UnrecoverableKeyException;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import javax.security.cert.X509Certificate;
import java.security.interfaces.RSAPrivateCrtKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.RSAPrivateCrtKeySpec;
import java.security.spec.RSAPublicKeySpec;
import java.util.Enumeration;

/**
 * 证书和 Key 的操作帮助类
 * @author Administrator
 *
 */
public class CertAndKeyHelper {
	/**
     * Java密钥库(Java 密钥库，JKS)KEY_STORE
     */
    //public static final String KEY_STORE_TYPE = "JKS";
    public static final String X509 = "X.509";
    public static final String KEY_STORE_TYPE = "PKCS12";

    /**
     * 
     * @param keyStorePath 密钥库存储路径
     * @param alias 
     * @param password 密钥库密码
     * @return
     * @throws IOException 
     * @throws CertificateException 
     * @throws NoSuchAlgorithmException 
     * @throws KeyStoreException 
     * @throws UnrecoverableKeyException 
     */
    public static PrivateKey getPrivateKey(String keyStorePath, String alias, String password) 
    		throws KeyStoreException, NoSuchAlgorithmException, CertificateException, IOException, UnrecoverableKeyException
    {
    	KeyStore keyStore = getKeyStore(keyStorePath, password);
    	PrivateKey privateKey = (PrivateKey) keyStore.getKey(alias, password.toCharArray());
    	return privateKey;
    }

    /**
     * 获取证书别名
     * @param keyStore
     * @return
     * @throws KeyStoreException 
     */
    public static String getKeyAlias(KeyStore keyStore) 
    		throws KeyStoreException
    {
    	String keyAlias = "";
	    Enumeration<String> enums = keyStore.aliases();
	    while (enums.hasMoreElements()) {
	    	keyAlias = enums.nextElement();
	    }
    	return keyAlias;
    }
	 /**
     * <p>
     * 获得密钥库
     * </p>
     * 
     * @param keyStorePath 密钥库存储路径
     * @param password 密钥库密码
     * @return
	 * @throws KeyStoreException 
	 * @throws IOException 
	 * @throws CertificateException 
	 * @throws NoSuchAlgorithmException 
     */
    public static KeyStore getKeyStore(String keyStorePath, String password) 
    		throws KeyStoreException, NoSuchAlgorithmException, CertificateException, IOException
    {
        FileInputStream fis = null;
        try
        {
        	fis = new FileInputStream(keyStorePath);
	        KeyStore keyStore = KeyStore.getInstance(KEY_STORE_TYPE);
	        keyStore.load(fis, password.toCharArray());
	        return keyStore;
        }
        finally
        {
        	if(fis != null)
        		fis.close();
        }
    }
    
    /**
     * <p>
     * 根据证书获得公钥
     * </p>
     * 
     * @param certificatePath 证书存储路径
     * @return
     * @throws IOException 
     * @throws CertificateException 
     */
    public static PublicKey getPublicKey(String certificatePath) 
    		throws CertificateException, IOException
    {
        Certificate certificate = getCertificate(certificatePath);
        PublicKey publicKey = certificate.getPublicKey();
        return publicKey;
    }

    /**
     * <p>
     * 获得证书
     * </p>
     * 
     * @param certificatePath 证书存储路径
     * @return
     * @throws CertificateException 
     * @throws IOException 
     */
    public static Certificate getCertificate(String certificatePath) 
    		throws CertificateException, IOException
    {
        CertificateFactory certificateFactory = CertificateFactory.getInstance(X509);
        FileInputStream fis = null;
        try
        {
	        fis = new FileInputStream(certificatePath);
	        Certificate certificate = certificateFactory.generateCertificate(fis);
	        return certificate;
        }
        finally
        {
        	if(fis != null)
        		fis.close();
        }
    }
    
    /**
     * <p>
     * 根据密钥库获得证书
     * </p>
     * 
     * @param keyStorePath 密钥库存储路径
     * @param alias 密钥库别名
     * @param password 密钥库密码
     * @return
     * @throws IOException 
     * @throws CertificateException 
     * @throws NoSuchAlgorithmException 
     * @throws KeyStoreException 
     */
    public static Certificate getCertificate(String keyStorePath, String alias, String password) 
    		throws KeyStoreException, NoSuchAlgorithmException, CertificateException, IOException
    { 
        KeyStore keyStore = getKeyStore(keyStorePath, password);
        Certificate certificate = keyStore.getCertificate(alias);
        return certificate;
    }
    /**
     * 将 RSA 公钥数据转换为 Xml 格式的字符串。
     * @param key 公钥
     * @return Xml 格式的字符串
     */
    public static String encodePublicKeyToXml(PublicKey key) 
    {
        if (!RSAPublicKey.class.isInstance(key)) {
            return null;
        }
        RSAPublicKey pubKey = (RSAPublicKey) key;
        //对长度进行初始化
        StringBuffer sb = new StringBuffer(320);
        sb.append("<RSAKeyValue>")
        	.append("<Modulus>")
                .append(Base64Helper.encode(pubKey.getModulus().toByteArray()))
            .append("</Modulus>")
            .append("<Exponent>")
                .append(Base64Helper.encode(pubKey.getPublicExponent().toByteArray()))
            .append("</Exponent>")
          .append("</RSAKeyValue>");
        return sb.toString();
    }
    /**
     * 
     * @param xmlKey
     * @return
     * @throws NoSuchAlgorithmException
     * @throws InvalidKeySpecException
     */
    public static PublicKey decodePublicKeyFromXml(String xmlKey) 
    		throws NoSuchAlgorithmException, InvalidKeySpecException
    {
    	xmlKey = xmlKey.replaceAll("\r", "").replaceAll("\n", "");
    	//获取 Modulus
    	String modulusString = StringHelper.GetMiddleString(xmlKey, "<Modulus>", "</Modulus>");
        BigInteger modulus = new BigInteger(1, Base64Helper.decode(modulusString));
        //获取 Exponent
        String exponentString = StringHelper.GetMiddleString(xmlKey, "<Exponent>", "</Exponent>");
        BigInteger publicExponent = new BigInteger(1, Base64Helper.decode(exponentString));

        //创建公钥对象
        RSAPublicKeySpec rsaPubKey = new RSAPublicKeySpec(modulus, publicExponent);
        
    	KeyFactory keyf = KeyFactory.getInstance("RSA");
        return keyf.generatePublic(rsaPubKey);
    }
    /**
     * 从 Xml 中获取私钥所需要的信息，创建私钥对象。 
     * @param xmlKey
     * @return
     * @throws NoSuchAlgorithmException 
     * @throws InvalidKeySpecException 
     */
    public static PrivateKey decodePrivateKeyFromXml(String xmlKey) 
    		throws NoSuchAlgorithmException, InvalidKeySpecException 
    {
        xmlKey = xmlKey.replaceAll("\r", "").replaceAll("\n", "");
        
        byte[] modulusBytes = Base64Helper.decode(StringHelper.GetMiddleString(xmlKey, "<Modulus>", "</Modulus>"));
        BigInteger modulus = new BigInteger(1, modulusBytes);
        
        byte[] exponentBytes = Base64Helper.decode(StringHelper.GetMiddleString(xmlKey, "<Exponent>", "</Exponent>"));
        BigInteger publicExponent = new BigInteger(1,exponentBytes);
        
        byte[] dBytes = Base64Helper.decode(StringHelper.GetMiddleString(xmlKey, "<D>", "</D>"));
        BigInteger privateExponent = new BigInteger(1, dBytes);
        
        byte[] pBytes = Base64Helper.decode(StringHelper.GetMiddleString(xmlKey, "<P>", "</P>"));
        BigInteger primeP = new BigInteger(1, pBytes);
        
        byte[] qBytes = Base64Helper.decode(StringHelper.GetMiddleString(xmlKey, "<Q>", "</Q>"));
        BigInteger primeQ = new BigInteger(1, qBytes);
        
        byte[] dpBytes = Base64Helper.decode(StringHelper.GetMiddleString(xmlKey, "<DP>", "</DP>"));
        BigInteger primeExponentP = new BigInteger(1, dpBytes);
        
        byte[] dqBytes = Base64Helper.decode(StringHelper.GetMiddleString(xmlKey, "<DQ>", "</DQ>"));
        BigInteger primeExponentQ = new BigInteger(1, dqBytes);
        
        byte[] inverseQBytes = Base64Helper.decode(StringHelper.GetMiddleString(xmlKey, "<InverseQ>", "</InverseQ>"));
        BigInteger crtCoefficient = new BigInteger(1, inverseQBytes);

        RSAPrivateCrtKeySpec rsaPriKey = new RSAPrivateCrtKeySpec(modulus,
                publicExponent, privateExponent, primeP, primeQ,
                primeExponentP, primeExponentQ, crtCoefficient);

    	KeyFactory keyf = KeyFactory.getInstance("RSA");
        return keyf.generatePrivate(rsaPriKey);
    }
    /**
     * 将私钥中的信息保存为 xml 格式字符串
     * @param key
     * @return
     */
	public static String encodePrivateKeyToXml(PrivateKey key) 
	{
        if (!RSAPrivateCrtKey.class.isInstance(key)) {
            return null;
        }
        RSAPrivateCrtKey priKey = (RSAPrivateCrtKey) key;
        //对长度进行初始化
        StringBuffer sb = new StringBuffer(1024);

        sb.append("<RSAKeyValue>")
        	.append("<Modulus>")
                .append(Base64Helper.encode(priKey.getModulus().toByteArray()))
            .append("</Modulus>")
        	.append("<Exponent>")
                .append(Base64Helper.encode(priKey.getPublicExponent().toByteArray()))
            .append("</Exponent>")
            .append("<P>")
                .append(Base64Helper.encode(priKey.getPrimeP().toByteArray()))
            .append("</P>")
        	.append("<Q>")
                .append(Base64Helper.encode(priKey.getPrimeQ().toByteArray()))
            .append("</Q>")
            .append("<DP>")
                .append(Base64Helper.encode(priKey.getPrimeExponentP().toByteArray()))
            .append("</DP>")
        	.append("<DQ>")
                .append(Base64Helper.encode(priKey.getPrimeExponentQ().toByteArray()))
            .append("</DQ>")
        	.append("<InverseQ>")
                .append(Base64Helper.encode(priKey.getCrtCoefficient().toByteArray()))
            .append("</InverseQ>")
        	.append("<D>")
                .append(Base64Helper.encode(priKey.getPrivateExponent().toByteArray()))
            .append("</D>")
        .append("</RSAKeyValue>");
        return sb.toString();
    }
}
